 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.core.internal.watson;

 import org.eclipse.core.internal.dtree.AbstractDataTreeNode;
 import org.eclipse.core.internal.dtree.DataTreeNode;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;

 /**
  * A class for performing operations on each element in an element tree.
  * For example, this can be used to print the contents of a tree.
  * <p>
  * When creating an ElementTree iterator, an element tree and root path must be
  * supplied. When the <code>iterate()</code> method is called, a visitor object
  * must be provided. The visitor is called once for each node of the tree. For
  * each node, the visitor is passed the entire tree, the object in the tree at
  * that node, and a callback for requesting the full path of that node.
  * <p>
  * <b>Example:</b>
  <code><pre>
  // printing a crude representation of the poster child
  IElementContentVisitor visitor=
  new IElementContentVisitor() {
  public void visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
  System.out.println(requestor.requestPath() + " -> " + elementContent);
  }
  });
  ElementTreeIterator iterator = new ElementTreeIterator(tree, Path.ROOT);
  iterator.iterate(visitor);
  </pre></code>
  */
 public class ElementTreeIterator implements IPathRequestor {
     //for path requestor
 private String [] segments = new String [10];
     private int nextFreeSegment;

     /* the tree being visited */
     private ElementTree tree;

     /* the root of the subtree to visit */
     private IPath path;

     /* the immutable data tree being visited */
     private DataTreeNode treeRoot;

     /**
      * Creates a new element tree iterator for visiting the given tree starting
      * at the given path.
      */
     public ElementTreeIterator(ElementTree tree, IPath path) {
         this.tree = tree;
         this.path = path;
         //treeRoot can be null if deleted concurrently
 //must copy the tree while owning the tree's monitor to prevent concurrent deletion while creating visitor's copy
 synchronized (tree) {
             treeRoot = (DataTreeNode) tree.getDataTree().safeCopyCompleteSubtree(path);
         }
     }

     /**
      * Iterates through the given element tree and visit each element (node)
      * passing in the element's ID and element object.
      */
     private void doIteration(DataTreeNode node, IElementContentVisitor visitor) {
         //push the name of this node to the requestor stack
 if (nextFreeSegment >= segments.length) {
             grow();
         }
         segments[nextFreeSegment++] = node.getName();

         //do the visit
 if (visitor.visitElement(tree, this, node.getData())) {
             //recurse
 AbstractDataTreeNode[] children = node.getChildren();
             for (int i = children.length; --i >= 0;) {
                 doIteration((DataTreeNode) children[i], visitor);
             }
         }

         //pop the segment from the requestor stack
 nextFreeSegment--;
         if (nextFreeSegment < 0)
             nextFreeSegment = 0;
     }

     /**
      * Method grow.
      */
     private void grow() {
         //grow the segments array
 int oldLen = segments.length;
         String [] newPaths = new String [oldLen * 2];
         System.arraycopy(segments, 0, newPaths, 0, oldLen);
         segments = newPaths;
     }

     /**
      * Iterates through this iterator's tree and visits each element in the
      * subtree rooted at the given path. The visitor is passed each element's
      * data and a request callback for obtaining the path.
      */
     public void iterate(IElementContentVisitor visitor) {
         if (path.isRoot()) {
             //special visit for root element to use special treeData
 if (visitor.visitElement(tree, this, tree.getTreeData())) {
                 if (treeRoot == null)
                     return;
                 AbstractDataTreeNode[] children = treeRoot.getChildren();
                 for (int i = children.length; --i >= 0;) {
                     doIteration((DataTreeNode) children[i], visitor);
                 }
             }
         } else {
             if (treeRoot == null)
                 return;
             push(path, path.segmentCount() - 1);
             doIteration(treeRoot, visitor);
         }
     }

     /**
      * Push the first "toPush" segments of this path.
      */
     private void push(IPath pathToPush, int toPush) {
         if (toPush <= 0)
             return;
         for (int i = 0; i < toPush; i++) {
             if (nextFreeSegment >= segments.length) {
                 grow();
             }
             segments[nextFreeSegment++] = pathToPush.segment(i);
         }
     }

     public String requestName() {
         if (nextFreeSegment == 0)
             return ""; //$NON-NLS-1$
 return segments[nextFreeSegment - 1];
     }

     public IPath requestPath() {
         if (nextFreeSegment == 0)
             return Path.ROOT;
         int length = nextFreeSegment;
         for (int i = 0; i < nextFreeSegment; i++) {
             length += segments[i].length();
         }
         StringBuffer pathBuf = new StringBuffer (length);
         for (int i = 0; i < nextFreeSegment; i++) {
             pathBuf.append('/');
             pathBuf.append(segments[i]);
         }
         return new Path(null, pathBuf.toString());
     }
 }

